import webapp2
import jinja2
import os
import datetime
import logging

from google.appengine.api import users
from google.appengine.ext import db
from datetime import date

# Calendar API includes
#from apiclient.discovery import build
#from apiclient.oauth import OAuthCredentials

#import httplib2
#import oauth2 as oauth

# gdata includes
import atom
import gdata.service
import gdata.auth
import gdata.alt.appengine
import gdata.calendar
import gdata.calendar.service

# Starting jinja environment for templating
jinja_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

class Event(db.Model):
    invitees = db.ListProperty(users.User)
    place = db.StringProperty()
    date = db.DateProperty(required=True)

    edit_link = db.TextProperty()
    gcal_event_link = db.TextProperty()
    gcal_event_xml = db.TextProperty()

class PokerMatch(Event):
    winner = db.UserProperty()
    
class PoolMatch(Event):
    winner = db.UserProperty()

class Set(db.Model):
    number = db.IntegerProperty()
    team1score = db.IntegerProperty()
    team2score = db.IntegerProperty()
    event = db.ReferenceProperty(Event)

class Player(db.Model):
    nickname = db.StringProperty()
    matchesWon = db.ReferenceProperty(Event,
        collection_name = 'matchesWon')
    matchesLost = db.ReferenceProperty(Event,
        collection_name = 'matchesLost')
    
class FoosballMatch(Event):
    players = db.ReferenceProperty(Player,
        collection_name = 'players')
    score1 = db.IntegerProperty()
    score2 = db.IntegerProperty()
    notes = db.TextProperty()
    
    #winnersRef = db.ReferenceProperty(Player,
    #    collection_name = 'winners')
    
    winners = db.ListProperty(db.Key)
    
    """def __init__(self, date, place, score1, score2, notes):
        self.date = date
        self.place = place
        self.score1 = score1
        self.score2 = score2
        self.notes = notes"""
        
    
class FoosballMatchPlayerResult(db.Model):
    player = db.ReferenceProperty(Player
                                   , required = True)

    match = db.ReferenceProperty(Event, required = True)
    
    won = db.BooleanProperty(required = True)
    
class Match(db.Model):
    date = db.DateProperty(required=True)
    winner = db.StringProperty()
    numPlayers = db.IntegerProperty()
    playerList = db.ListProperty(users.User)

class Tournament(db.Model):
    name = db.StringProperty(required=True)
    type = db.StringProperty(required=True)
    
    @property
    def getkey(self):
        return self.key()

class Index(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        
        # Get the list of tournaments to display
        tournamentsQuery = Tournament.all()
        tournaments = tournamentsQuery.run()
        
        args = {
            "isAdmin" : users.is_current_user_admin()
            , "logoutUrl" : users.create_logout_url("/")
            , "name" : user.nickname()
            , "email" : user.email()
            , "tournaments" : tournaments
        }
        
        template = jinja_environment.get_template('index.html')
        self.response.out.write(template.render(args))
    
class Index2(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        #match key and date, to be displayed on the welcome page
        playedMatchList, unplayedMatchList = [], []
        #matches = Match.all()
        matches = FoosballMatch.all()
        for match in matches:
            if match.winner1 or match.winner2:
                playedMatchList.append((match.key(), match.date))
            else:
                unplayedMatchList.append((match.key(), match.date))
        
        args = {
                "isAdmin" : users.is_current_user_admin()
                , "logoutUrl" : users.create_logout_url("/")
                , "name" : user.nickname()
                , "email" : user.email()
                , "playedMatchList" : playedMatchList
                , "unplayedMatchList" : unplayedMatchList
                }
        
        template = jinja_environment.get_template('index.html')
        self.response.out.write(template.render(args))

class CreateTournament(webapp2.RequestHandler):
    def post(self):
        nameArg = self.request.get('newTournamentName')
        typeArg = self.request.get('newTournamentType')
        
        if nameArg and typeArg:
            newTournament = Tournament(name=nameArg, type=typeArg)
            newTournament.put()
        else:
            logging.error('New tournament name or type were not provided.')
        
        self.redirect('/')

class GetTournament(webapp2.RequestHandler):
    def get(self):
        key = self.request.get("key")
        tKey = db.Key(key)
        
        if key:
            #tournament = Tournament.get_by_key_name(key)
            tournament = Tournament.get(tKey)
            
            if tournament:
                if tournament.type == "foosball":
                    
                    # Get the list of matches to display
                    matchesQuery = FoosballMatch.all()
                    matches = matchesQuery.run()
        
                    # Get the list of results
                    resultsQuery = FoosballMatchPlayerResult.all()
                    results = resultsQuery.run()
                    
                    #Aggregate the results
                    
                    
                    user = users.get_current_user()
                    
                    args = {
                        "tournamentName" : tournament.name
                        , "matches" : matches
                        , "players" : players
                        , "isAdmin" : users.is_current_user_admin()
                        , "logoutUrl" : users.create_logout_url("/")
                        , "name" : user.nickname()
                        , "email" : user.email()
                    }
                    
                    template = jinja_environment.get_template('foosballTournament.html')
                    self.response.out.write(template.render(args))
                if tournament.type == "poker":
                    pass
                if tournament.type == "pool":
                    pass
                if tournament.type == "tableTennis":
                    pass
            else:
                logging.error("No tournament found with key %s", key)
        else:
            logging.error("Invalid tournament key")

class AddFoosMatch(webapp2.RequestHandler):
    def get(self):
        dateArg = self.request.get("d")
        date = ""
        
        if dateArg:
            date = datetime.date(int(dateArg.split('-')[0]), int(dateArg.split('-')[1]), int(dateArg.split('-')[2]))
        
        # Create new players if they do not exist
        player1 = Player.get_or_insert(self.request.get("p1"))
        player2 = Player.get_or_insert(self.request.get("p2"))
        player3 = Player.get_or_insert(self.request.get("p3"))
        player4 = Player.get_or_insert(self.request.get("p4"))
        
        # Construct FoosballMatch entity
        location = self.request.get("loc")
        winner1 = player1
        winner2 = player2
        loser1 = player3
        loser2 = player4
        
        score1, score2 = self.request.get("s1"), self.request.get("s2")
        if score1 and score2:
            if int(score2) > int(score1):
                winner1 = player3
                winner2 = player4
                loser1 = player1
                loser2 = player2
        
        match = FoosballMatch(
            date = date
            , location = location
            , score1 = int(score1)
            , score2 = int(score2)
            , notes = self.request.get("n")
            )
        match.put()
        
        logging.info("Foosball match saved with key %s", match.key().id())
        
        matchResult = FoosballMatchPlayerResult(player = winner1
                                                , match = match
                                                , won = True)
        matchResult.put()
        
        matchResult = FoosballMatchPlayerResult(player = winner2
                                                , match = match
                                                , won = True)
        matchResult.put()
                                  
        matchResult = FoosballMatchPlayerResult(player = loser1
                                                , match = match
                                                , won =False)
        matchResult.put()
        
        matchResult = FoosballMatchPlayerResult(player = winner1
                                                , match = match
                                                , won = False)
        matchResult.put()
        
        # TODO: implement XG transactions to atomically create matches and update player match lists
        
        # Prepare response for page
        self.response.headers["added"] = "1"

class CreateMatch(webapp2.RequestHandler):
    def get(self):
        # list last 80 years, starting from this year
        years = []
        thisYear = date.today().year
        for i in range(0, 80):
            years.append(thisYear - i)
        
        user = users.get_current_user()
        
        args = {
                "isAdmin" : users.is_current_user_admin()
                , "logoutUrl" : users.create_logout_url("/")
                , "name" : user.nickname()
                , "email" : user.email()
                , "years" : years
                }
        
        template = jinja_environment.get_template('createMatch.html')
        self.response.out.write(template.render(args))

class DeleteMatch2(webapp2.RequestHandler):
    def get(self):
        matchKeyStr = self.request.get("matchKey")
        
        if matchKeyStr:
            matchKey = db.Key(encoded=matchKeyStr)
            db.delete(matchKey)
        else:
            logging.error("Invalid event key. %s", matchKeyStr)
            
class DeleteMatch(webapp2.RequestHandler):
    def get(self):
        matchKey = self.request.get("matchKey")
        
        if matchKey:
            Match.delete(matchKey)
        else:
            logging.error("Invalid event key. %s", matchKey)
        
class SaveMatch(webapp2.RequestHandler):
    def setMatchProps(self, match, date, winner1, winner2, numPlayers):
        match.date = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
        
        if winner1 and winner2:
            matchWinner1 = MatchWinner(player=winner1, match=match)
            match.winner1 = winner1
            matchWinner2 = MatchWinner(player=winner2, match=match)
            match.winner2 = winner2
            
        if numPlayers:
            match.numPlayers = int(numPlayers)
        else:
            match.numPlayers = None
        
    def post(self):
        matchKey = db.Key(encoded=self.request.get("key"))
        #match = Match.get(matchKey)
        match = FoosballMatch.get(matchKey)
        if match:
            self.setMatchProps(match, self.request.get("date"), self.request.get("winner1"), self.request.get("winner2"), self.request.get("numPlayers"))
            match.put()
        
        self.redirect("/")

# Creates a match instance
# Arguments:
#  - day, month, year: match date
#  - playerList: list of emails to send the invitation

class DoCreateMatch(webapp2.RequestHandler):
    def post(self):
        #newMatch = Match(date=datetime.date(int(self.request.get("year")), int(self.request.get("month")), int(self.request.get("day"))))
        newMatch = FoosballMatch(date=datetime.datetime(int(self.request.get("year")), int(self.request.get("month")), int(self.request.get("day"))))
        newMatch.put()
        self.redirect("/")

class ViewMatch(webapp2.RequestHandler):
    def get(self):
        matchKey = db.Key(encoded=self.request.get("key"))
        #match = Match.get(matchKey)
        match = FoosballMatch.get(matchKey)
        matchProps = {}
        for prop in match.properties():
            matchProps[prop] = getattr(match, prop)
        
        user = users.get_current_user()
        
        #if current user is on the playerlist, disable signup button
        disableSignUp = False
        if user in match.invitees:
            disableSignUp = True
        
        args = {
                "isAdmin" : users.is_current_user_admin()
                , "logoutUrl" : users.create_logout_url("/")
                , "name" : user.nickname()
                , "email" : user.email()
                , "matchProps" : matchProps
                , "key" : self.request.get("key")
                , "disableSignUp": disableSignUp
                }
        
        template = jinja_environment.get_template('match.html')
        self.response.out.write(template.render(args))

# Adds a player to a match list of players
# To be called by AJAX
class SignUp(webapp2.RequestHandler):
    def post(self):
        matchKey = db.Key(encoded=self.request.get("matchKey"))
        #match = Match.get(matchKey)
        match = FoosballMatch.get(matchKey)
        self.response.headers["added"] = "0"
        if match:
            # Check if a player is adding himself, or manually adding another one
            if self.request.get("newPlayer"):
                user = users.User(str(self.request.get("newPlayer")))
            else:
                user = users.get_current_user()
                
            if user:
                if user not in match.invitees:
                    match.invitees.append(user)
                    match.put()
                    self.response.headers["added"] = "1"
                    self.response.headers["username"] = user.nickname()
                    #self.response.headers["userID"] = user.user_id()
                else:
                    logging.info("user already signed up!")
            else:
                logging.error("no user")
        else:
            logging.error("no match")

# Removes a player from the list of players
# To be called by AJAX
class SignOut(webapp2.RequestHandler):
    def post(self):
        matchKey = db.Key(encoded=self.request.get("matchKey"))
        #match = Match.get(matchKey)
        match = FoosballMatch.get(matchKey)
        self.response.headers["removed"] = "0"
        if (match):
            user = users.get_current_user()
            if (user):
                if user in match.invitees:
                    match.invitees.remove(user)
                    match.put()
                    self.response.headers["removed"] = "1"
                    self.response.headers["username"] = user.nickname()
                    self.response.headers["userID"] = user.user_id()
                else:
                    logging.info(user.nickname() + " not in the player list for match " + self.request.get("matchKey"))
            else:
                logging.info("no such user to be removed")
        else:
            logging.info("no match with key " + self.request.get("matchKey"))
            
# Creates a calendar entry for the match and sends an invitation to a mailing list
class SendInvitation(webapp2.RequestHandler):
    def __init__(self, request=None, response=None):
        self.initialize(request, response)
        
        # Create a Google Calendar client to talk to the Google Calendar service.
        self.calendar_client = gdata.calendar.service.CalendarService()
        # Modify the client to search for auth tokens in the datastore and use
        # urlfetch instead of httplib to make HTTP requests to Google Calendar.
        gdata.alt.appengine.run_on_appengine(self.calendar_client)
        
    def get(self):
        token_request_url = None

        # Find an AuthSub token in the current URL if we arrived at this page from
        # an AuthSub redirect.
        logging.info(self.request.uri)
        auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
        if auth_token:
            self.calendar_client.SetAuthSubToken(
                self.calendar_client.upgrade_to_session_token(auth_token))

        # Check to see if the app has permission to write to the user's
        # Google Calendar.
        if not isinstance(self.calendar_client.token_store.find_token(
                'http://www.google.com/calendar/feeds/'),
                gdata.auth.AuthSubToken):
            token_request_url = gdata.auth.generate_auth_sub_url(self.request.uri,
               ('http://www.google.com/calendar/feeds/default/',))
            return self.redirect(token_request_url.to_string())
            
        # Adds an event to Google Calendar
        
        event_entry = gdata.calendar.CalendarEventEntry()
        event_entry.title = atom.Title(text="Poker night at Joao & Luis's")
        event_entry.content = atom.Content(text="Let's play poker until we I get all of your money!")
        
        # cron runs on Mondays, games are on tuesdays
        # add one day to the current date and set the game to the starting time defined in the configuration file
        now = datetime.date.today()
        event_start_time = split(os.environ[EVENT_START_TIME], ':')
        if len(event_start_time) == 3:
            start_time = '%s.000Z' % datetime.datetime(now.year, now.month, now.day, event_start_time[0], event_start_time[1], event_start_time[2]).isoformat()
        else:
            start_time = '%s.000Z' % datetime.datetime(now.year, now.month, now.day, 20, 15, 00).isoformat()
        event_entry.when.append(gdata.calendar.When(start_time=start_time))
        event_entry.where.append(
                                 gdata.calendar.Where(value_string="joaoandluis"))
        new_attendee = gdata.calendar.Who()
        new_attendee.email = 'mail@joaoandremartins.com'
        event_entry.who.append(new_attendee)
        new_attendee = gdata.calendar.Who()
        new_attendee.email = "jampokergroup@googlegroups.com"
        event_entry.who.append(new_attendee)
        
        try:
            cal_event = self.calendar_client.InsertEvent(event_entry,
                'http://www.google.com/calendar/feeds/default/private/full')
            edit_link = cal_event.GetEditLink()
            logging.info("edit link: " + edit_link)
            
            #if edit_link and edit_link.href:
                #match.edit_link = edit_link.href
            
            """alternate_link = cal_event.GetHtmlLink()
                if alternate_link and alternate_link.href:
                    # Add a link to the event in the Google Calendar HTML web UI.
                    event.gcal_event_link = alternate_link.href
                    event.gcal_event_xml = str(cal_event)
                event.put()"""
            # If adding the event to Google Calendar failed due to a bad auth token,
            # remove the user's auth tokens from the datastore so that they can
            # request a new one.
        except gdata.service.RequestError, request_exception:
            logging.error(str(request_exception[0]['status']) + " " + str(request_exception[0]["reason"]) + " " + str(request_exception[0]["body"]))
            request_error = request_exception[0]
            if request_error['status'] == 401 or request_error['status'] == 403:
                gdata.alt.appengine.save_auth_tokens({})
                
            # If the request failure was not due to a bad auth token, reraise the
            # exception for handling elsewhere.
            else:
                raise

class ClearAuthToken(webapp2.RequestHandler):
    def get(self):
        gdata.alt.appengine.save_auth_tokens({})
        

app = webapp2.WSGIApplication([('/', Index)
                               , ('/createTournament', CreateTournament)
                               , ('/getTournament', GetTournament)
                               , ('/addMatch', AddFoosMatch)
                               , ('/createMatch', CreateMatch)
                               , ('/deleteMatch', DeleteMatch)
                               , ('/saveMatch', SaveMatch)
                               , ('/doCreateMatch', DoCreateMatch)
                               , ('/match', ViewMatch)
                               , ('/signUp', SignUp)
                               , ('/signOut', SignOut)
                               , ('/sendInvitation', SendInvitation)
                               , ('/clearAuthToken', ClearAuthToken)
                               ],
                              debug=True)
